/****

    activequant - activestocks.eu

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License along
    with this program; if not, write to the Free Software Foundation, Inc.,
    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

	
	contact  : contact@activestocks.eu
    homepage : http://www.activestocks.eu

****/
package org.activequant.core.domainmodel.account;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.activequant.core.types.TimeStamp;
import org.activequant.util.tools.ArrayUtils;
import org.apache.log4j.Logger;


/**
 * holds balance entries for an account. Use setCurrentBalance(..) or double constructor 
 * to initially seed the account. Add new BalanceEntries to record following cash flows. <br/>
 * Holds the following associated variables:
 * <ul>
 * <li>id(Long)</li>
 * <li>currentBalance(double)</li>
 * <li>balanceEntries(List&lt;BalanceEntry&gt;)</li>
 * </ul>
 * <br>
 * <b>History:</b><br> 
 *  - [03.05.2007] Created (Ulrich Staudinger)<br> 
 *  - [06.05.2007] Cleanup (Erik Nijkamp)<br>
 *  - [08.06.2007] Renaming and extending (Ulrich)<br>
 *  - [14.07.2007] Added persistence (Erik Nijkamp)<br>
 *  - [29.09.2007] Removed annotations + balance (Erik Nijkamp)<br>
 *  - [07.05.2008] Fixing 300-76 (Ulrich Staudinger)<br>
 * 
 * 
 * @author Ulrich Staudinger
 */
public class BalanceBook implements Serializable {
	
	private static final long serialVersionUID = 8793474828232233486L;
	/**
	 * protected final static Logger log = Logger.getLogger(BalanceBook.class);
	 */
	protected final static Logger log = Logger.getLogger(BalanceBook.class);
	/**
	 * private Long id;
	 */
	private Long id;
	/**
	 * private double currentBalance = 0.0;
	 */
	private double currentBalance = 0.0;
	/**
	 * private List&lt;BalanceEntry&gt; balanceEntries = new ArrayList&lt;BalanceEntry&gt;();
	 */
	private List<BalanceEntry> balanceEntries = new ArrayList<BalanceEntry>();
	/**
	 * an empty BalanceBook constructor 
	 */
	public BalanceBook() {

	}
	/**
	 * constructs a BalanceBook using the given balanceEntries(BalanceEntry...) to set its associated balanceEntries(List&lt;BalanceEntry&gt;)
	 * @param balanceEntries
	 */
	public BalanceBook(BalanceEntry... balanceEntries) {
		this.setBalanceEntries(balanceEntries);
	}
	/**
	 * constructs a BalanceBook using the given balance(double) and balanceEntries(BalanceEntry...) to set its associated currentBalance(double) and balanceEntries(List&lt;BalanceEntry&gt;)
	 * @param balance
	 * @param balanceEntries
	 */
	public BalanceBook(double balance, BalanceEntry... balanceEntries) {
		this.currentBalance = balance;
		this.setBalanceEntries(balanceEntries);
	}
	/**
	 * returns the associated id(Long)
	 * @return
	 */
	public Long getId() {
		return id;
	}
	/**
	 * sets the associated id(Long) with the given id(Long)
	 * @param id
	 */
	public void setId(Long id) {
		this.id = id;
	}
	/**
	 * returns whether the associated id(Long) is not <code>null</code>
	 * @return
	 */
	public boolean hasId() {
		return id != null;
	}

	/**
	 * sets the current balance of this balance book without generating a balance entry. 
	 * Use this method to initially seed the account. <br/>
	 * sets the associated currentBalance(double) with the given balance(double)
	 * @param balance
	 */
	public void setCurrentBalance(double balance) {
		this.currentBalance = balance;
	}
	/**
	 * returns the associated currentBalance(double)
	 * @return
	 */
	public double getCurrentBalance() {
		return currentBalance;
	}
	/**
	 * <code>1.</code> Adds the given value(double) to the associated currentBalance(double)<br/>
	 * <code>2.</code> If there is a BalanceEntry whose stamp(TimeStamp) matches the given date(TimeStamp), it adds given value(double) to its value(double). Otherwise,
	 * it just adds a new BalanceEntry with the given value(double) and date(TimeStamp) to the associated balanceEntries(List&lt;BalanceEntry&gt;)
	 * @param value
	 * @param date
	 */
	public void addBalanceEntry(double value, TimeStamp date) {
		this.currentBalance += value;
		// check if there is a balance entry for this specific date already. 
		for(BalanceEntry entry : balanceEntries){
			if(entry.getTimeStamp().equals(date)){
				entry.setValue(entry.getValue()+value);
				return;
			}
		}
		// still here, no existing entry found. 
		balanceEntries.add(new BalanceEntry(value, date));
	}
	/**
	 * returns the associated balanceEntries(List&lt;BalanceEntry&gt;) as BalanceEntry[] array
	 * @return
	 */
	public BalanceEntry[] getBalanceEntries() {
		return balanceEntries.toArray(new BalanceEntry[] {});
	}
	/**
	 * sets the associated balanceEntries(List&lt;BalanceEntry&gt;) with the given balanceEntries(List&lt;BalanceEntry&gt;)
	 * @param balanceEntries
	 */
	public void setBalanceEntries(List<BalanceEntry> balanceEntries) {
		this.balanceEntries = balanceEntries;
	}
	/**
	 * sets the associated balanceEntries(List&lt;BalanceEntry&gt;) with the given balanceEntries(BalanceEntry...)
	 * @param balanceEntries
	 */
	public void setBalanceEntries(BalanceEntry... balanceEntries) {
		this.balanceEntries = ArrayUtils.asList(balanceEntries);
	}
	/**
	 * returns an Iterator&lt;BalanceEntry&gt; for the associated balanceEntries(List&lt;BalanceEntry&gt;)
	 * @return
	 */
	public Iterator<BalanceEntry> getBalanceEntryIterator(){
		return balanceEntries.iterator();
	}
	/**
	 * returns a double[] containing the value(double) of every BalanceEntry in the associated balanceEntries(List&lt;BalanceEntry&gt;)
	 * @return
	 */
	public double[] getEquityCurve() {
		assert (balanceEntries.size() > 0);
		double[] curve = new double[balanceEntries.size()];
		int idx = 0;
		for (BalanceEntry entry : balanceEntries) {
			curve[idx++] = entry.getValue();
		}
		return curve;
	}

	/**
	 * iterates over all balance entries and returns the one with the biggest value<br/>
	 * returns the BalanceEntry with the biggest value(double) among the associated balanceEntries(List&lt;BalanceEntry&gt;)
	 * @return A Balance Entry
	 */
	public BalanceEntry getBiggestGain() {
		assert (balanceEntries.size() > 0);
		BalanceEntry ret = balanceEntries.get(0);
		for (BalanceEntry t : balanceEntries) {
			if (t.getValue() > ret.getValue())
				ret = t;
		}
		return ret;
	}

	/**
	 * iterates over all balance entries and returns the one with the smallest value <br/>
	 * returns the BalanceEntry with the smallest value(double) among the associated balanceEntries(List&lt;BalanceEntry&gt;)
	 * @return a Balance Entry
	 */
	public BalanceEntry getBiggestLoss() {
		assert (balanceEntries.size() > 0);
		BalanceEntry ret = balanceEntries.get(0);
		for (BalanceEntry t : balanceEntries) {
			if (t.getValue() < ret.getValue())
				ret = t;
		}
		return ret;
	}
	/**
	 * returns the initial balance which is calculated by subtracting/negating the value(double) of every BalanceEntry in the associated balanceEntries(List&lt;BalanceEntry&gt;)
	 * from the associated currentBalance(double) value
	 * @return
	 */
	public double getInitialBalance(){
		double ret = currentBalance;
		double[] curve = getEquityCurve();
		for(int i=getEquityCurve().length-1;i>0;i--){
			ret-=curve[i];
		}
		return ret; 
	}
	
}
